Modern computing environments generally provide for multiple sets of computer instructions to execute concurrently. For example, a computing environment may permit a single program to spawn two or more “threads,” where the threads execute in parallel, or two computer programs may execute concurrently under the control of a multi-tasking operating system. When the environment permits multiple sets of instructions to execute concurrently, it is frequently the case that these different sets of instructions need shared access to a resource, such as a data object. For example, two threads of a program may both need to read from, and write to, a data structure located within the program's address space, or two programs, each with its own address space, may share data through the operating system's file system.
A problem that arises when a resource is accessible to several threads that execute concurrently is that unpredictable, and sometimes disastrous, results can be produced if one thread is permitted to read from a resource while another thread is writing to that same resource. If reading and writing threads have simultaneous access to a resource, then the writer would be able to change the contents of the resource while the reader is in the process of reading the contents. For example, a resource could be a file of English text, and a program having a buffer that is only half the size of the text reads the file in two stages. If, between these two stages, a thread reading the file is interrupted by a writing thread that changes the contents of the file, then the portion read in the second stage may no longer correspond to the portion read in the first stage, resulting in the program receiving unreadable gibberish. As another example, a user-defined data structure could represent high-precision floating-point numbers, where the mantissa is stored in the first two words of the structure, and the exponent in the last two words. A software routine that performs an arithmetic operation (e.g., multiplication) on these numbers may read the mantissa and the exponent separately. If a thread reading the number is interrupted by a writing thread between the times that the mantissa and exponent are read, then the arithmetic operation will be performed on a number fabricated from the mantissa of the old value and the exponent of the new value. In all likelihood, this fabricated number will not represent any useful value.
One solution to the problem of concurrent access to a resource is to use a “locking/unlocking” mechanism. With such a mechanism, a portion of a computer program that accesses a data object begins with a “lock” instruction, which notifies other threads that the object is in use and should not be accessed by other threads. The portion of the code that needs to access the object concludes with an “unlock”instruction, which notifies other threads that the object is once again available for use. This solution, however, has drawbacks. First, the use of a general “lock/unlock” mechanism is overbroad, in that it needlessly prevents multiple threads from reading an object simultaneously, even though the mischief caused by concurrent access is usually limited to the situation where a read and a write operation (or multiple write operations) are taking place at the same time. Second, a programmer who uses this mechanism must conscientiously include an “unlock” instruction corresponding to every “lock” instruction. If the programmer fails to unlock a data object that has been locked, the program will likely “freeze up” during execution, because other threads that require access to the object will not be able to proceed while the first thread has permanently tied up (locked) the object.
Various systems that address the problem of concurrent access to a resource permit several read accesses to take place at the same time, while allowing each write access to take place on an exclusive basis. Such systems are disclosed in the following U.S. Pat. No. 4,249,241 (Aberle, et al.); U.S. Pat. No. 5,161,227 (Dias, et al.); U.S. Pat. No. 5,226,143 (Baird, et al.); U.S. Pat. No. 5,285,528 (Hart); U.S. Pat. No. 5,454,108 (Devarakonda, et al.); U.S. Pat. No. 5,761,659 (Bertoni); U.S. Pat. No. 5,909,695 (Wong, et al.); U.S. Pat. No. 5,931,919 (Thomas, et al.); U.S. Pat. No. 5,940,828 (Anaya, et al.). However, these patents do not disclose that their systems are compatible for use with multiple threads in a single computer program, or in the other related contexts described herein.
In view of foregoing, there is a recognized need for a mechanism that overcomes the limitations and drawbacks noted above. No such mechanism has been realized in the prior art.